
Imports System.Net.Sockets
Imports System.Net

Public Class tcpTalk

    Private Shared ReadOnly TCPTALK_REQUEST_COUNTER As Int64
    Private Shared ReadOnly TCPTALK_RESPONSE_TIMEMS As Int64
    Public Const defaultHoldTime As Int64 = tcpServer2.defaultHoldTime
    Public Const defaultWaitTime As Int64 = tcpServer2.defaultWaitTime
    Public Const defaultLiveTime As Int64 = tcpServer2.defaultLiveTime
    Private _host As String
    Private _port As Integer
    Private _data As nettransData
    Private _tcpClient As TcpClient
    Private _startTime As Int64
    Private _receive As nettransData
    Public livetime As Int64                    'time of a tcp connection live, before resetconnect
    Public holdtime As Int64                    'time of each send and receive, will use double when talk
    Private _localaddress As String
    Private _localport As Int32
    Private _blanktime As Int64

    Public Function blanktime() As Int64
        Return _blanktime
    End Function

    Public Function localIPEndpoint() As Net.IPEndPoint
        Return convertor.cast(Of Net.IPEndPoint)(_tcpClient.Client.LocalEndPoint())
    End Function

    Public Function localaddress() As String
        Return _localaddress
    End Function

    Public Function localport() As Int32
        Return _localport
    End Function

    Public Function tcpClient() As TcpClient
        Return _tcpClient
    End Function

    Public Property host() As String
        Get
            Return _host
        End Get
        Set(ByVal value As String)
            If _host <> value Then
                _host = value
                resetConnection()
            End If
        End Set
    End Property

    Public Property port() As Integer
        Get
            Return _port
        End Get
        Set(ByVal value As Integer)
            If _port <> value Then
                _port = value
                resetConnection()
            End If
        End Set
    End Property

    Public Function remoteIPEndpoint() As Net.IPEndPoint
        Return convertor.cast(Of Net.IPEndPoint)(_tcpClient.Client.RemoteEndPoint)
    End Function

    Public Property sendData() As nettransData
        Get
            Return _data
        End Get
        Set(ByVal value As nettransData)
            If Not Object.ReferenceEquals(value, _data) Then
                copy(_data, value)
            End If
        End Set
    End Property

    Public Function startTime() As Int64
        Return _startTime
    End Function

    Public Function receiveData() As nettransData
        Return _receive
    End Function

#If RETIRED Then
    Public Function send(Of valueT As IConvertible)(ByVal host As String, ByVal port As Integer _
                                                    , ByVal value As valueT) As Boolean
        If _data Is Nothing Then
            _data = New nettransData
        Else
            _data.clear()
        End If
        _data.insert(value)
        Return send(host, port, _data)
    End Function

    Public Function send(ByVal host As String, ByVal port As Integer, ByVal data As nettransData) As Boolean
        Me.host = host
        Me.port = port
        copy(_data, data)
        Return send()
    End Function
#End If

    Private Function send(Optional ByVal needOvertimeInfo As Boolean = True) As Boolean
        On Error Resume Next

        _startTime = ticksToMilliseconds(Now.Ticks)
        If _tcpClient Is Nothing OrElse tcpTrans.clientClosed(_tcpClient) OrElse _
           ticksToMilliseconds(Now.Ticks) - _blanktime > livetime Then
            resetConnection()
            _tcpClient.Connect(_host, _port)
        End If

        _tcpClient.SendTimeout = holdtime
        _tcpClient.ReceiveTimeout = holdtime
        While tcpTrans.clientClosed(_tcpClient) AndAlso ticksToMilliseconds(Now.Ticks) - _startTime < holdtime
            sleep(defaultWaitTime)
            _tcpClient.Connect(_host, _port)
        End While
        _localaddress = Net.IPAddress.Parse(localIPEndpoint().Address.ToString()).ToString()
        _localport = localIPEndpoint().Port

        If tcpTrans.send(_tcpClient, _data, holdtime - (ticksToMilliseconds(Now.Ticks) - _startTime)) Then
            _blanktime = ticksToMilliseconds(Now.Ticks)
            Return True
        Else
            If needOvertimeInfo Then
                raiseError("cannot send with client, over holdtime." _
                            + " host " + displayHostPort(host, port), errorHandle.errorType.system)
            End If
            resetConnection()
            Return False
        End If
    End Function

    Public Function talk(Optional ByVal needOvertimeInfo As Boolean = True) As Boolean
        Dim extCode As Boolean = False
        Dim holdtime As Int64 = Me.holdtime << 1

        Dim handleTCPTALK_REQUEST_COUNTER As Boolean
        handleTCPTALK_REQUEST_COUNTER = (rnd(0, 1) <= 0.01)
        If handleTCPTALK_REQUEST_COUNTER Then
            counter.increase(TCPTALK_REQUEST_COUNTER)
        End If
        If send(needOvertimeInfo) Then
            _receive.clear()
            If tcpTrans.receive(_tcpClient, _receive, _
                                holdtime - (ticksToMilliseconds(Now.Ticks) - _startTime)) Then
                _blanktime = ticksToMilliseconds(Now.Ticks)
                extCode = True
            Else
                If needOvertimeInfo Then
                    raiseError("cannot talk with client, over holdtime." _
                                + " host " + displayHostPort(host, port), errorHandle.errorType.system)
                End If
                resetConnection()
                extCode = False
            End If
        Else
            resetConnection()
            extCode = False
        End If
        counter.increase(TCPTALK_RESPONSE_TIMEMS, ticksToMilliseconds(Now.Ticks()) - _startTime)
        If handleTCPTALK_REQUEST_COUNTER Then
            counter.decrease(TCPTALK_REQUEST_COUNTER)
        End If

        Return extCode
    End Function

    Public Sub resetConnection()
        On Error Resume Next

        tcpTrans.closeTcpClient(_tcpClient)
        _tcpClient = New TcpClient()
        tcpTrans.initialTcpClient(_tcpClient)
        _blanktime = 0
    End Sub

    Public Sub reset()
        _host = Nothing
        _port = 0
        holdtime = defaultHoldTime
        livetime = defaultLiveTime
        If Not _data Is Nothing Then
            _data.clear()
        Else
            _data = New nettransData
        End If
        resetConnection()
        _startTime = 0
        If Not _receive Is Nothing Then
            _receive.clear()
        Else
            _receive = New nettransData()
        End If
    End Sub

    Shared Sub New()
        TCPTALK_REQUEST_COUNTER = counter.register("TCPTALK_REQUEST_COUNTER")
        TCPTALK_RESPONSE_TIMEMS = counter.registerAverageAndLastAverage("TCPTALK_RESPONSE_TIMEMS")
    End Sub

    Public Sub New()
        reset()
    End Sub

    Protected Overrides Sub Finalize()
        tcpTrans.closeTcpClient(_tcpClient)
        GC.KeepAlive(_tcpClient)
        MyBase.Finalize()
    End Sub

    Public Function display() As String
        Return displayHostPort(host, port)
    End Function

    Public Shared Function display(ByVal t As tcpTalk) As String
        If t Is Nothing Then
            Return Nothing
        Else
            Return t.display()
        End If
    End Function
End Class

Public Class talkpool_t
    Inherits pool(Of tcpTalk, _true, _false)

    Private Shared TCPTALK_POOL_LENGTH_COUNTER As Int64

    Protected Overrides Function create() As tcpTalk
        counter.increase(TCPTALK_POOL_LENGTH_COUNTER)
        Return New tcpTalk()
    End Function

    Protected Overrides Sub unalloc(ByRef inst As tcpTalk)
        If Not inst Is Nothing Then
            inst.reset()
        End If
        counter.decrease(TCPTALK_POOL_LENGTH_COUNTER)
        MyBase.unalloc(inst)
    End Sub

    Shared Sub New()
        TCPTALK_POOL_LENGTH_COUNTER = counter.register("TCPTALK_POOL_LENGTH_COUNTER")
    End Sub

    Public Sub New(Optional ByVal buffcount As Int64 = defaultBuffCount)
        MyBase.New(buffcount)
    End Sub
End Class

Public Class talkpoolDefinition
    Inherits talkpool_t

    Public Sub New(Optional ByVal buffcount As Int64 = defaultBuffCount)
        MyBase.new(buffcount)
    End Sub
End Class

Public Class fixpoint_talkpool_t
    Inherits talkpool_t

    Private host As String = Nothing
    Private port As Int32 = 0
    Private holdtime As Int64 = tcpTalk.defaultHoldTime
    Private livetime As Int64 = tcpTalk.defaultLiveTime

    Protected Overrides Function create() As tcpTalk
        Dim rtn As tcpTalk = Nothing
        rtn = MyBase.create()
        If Not rtn Is Nothing Then
            rtn.host = host
            rtn.port = port
            rtn.holdtime = holdtime
            rtn.livetime = livetime
        End If

        Return rtn
    End Function

    Private Sub _new(ByVal ip As IPEndPoint)
        _new(Convert.ToString(ip.Address), ip.Port)
    End Sub

    Private Sub _new(ByVal host As String, ByVal port As Int32)
        copy(Me.host, host)
        copy(Me.port, port)
    End Sub

    Private Sub _new(ByVal holdtime As Int64, ByVal livetime As Int64)
        Me.holdtime = holdtime
        Me.livetime = livetime
    End Sub

    Private Sub _new(ByVal ip As IPEndPoint, ByVal holdtime As Int64, ByVal livetime As Int64)
        _new(ip)
        _new(holdtime, livetime)
    End Sub

    Private Sub _new(ByVal host As String, ByVal port As Int32, ByVal holdtime As Int64, ByVal livetime As Int64)
        _new(host, port)
        _new(holdtime, livetime)
    End Sub

    Public Sub New(ByVal host As String, ByVal port As Int32, Optional ByVal buffcount As Int64 = defaultBuffCount)
        MyBase.New(buffcount)
        _new(host, port)
    End Sub

    Public Sub New(ByVal host As String, ByVal port As Int32, ByVal holdtime As Int64, ByVal livetime As Int64, _
                   Optional ByVal buffcount As Int64 = defaultBuffCount)
        MyBase.New(buffcount)
        _new(host, port, holdtime, livetime)
    End Sub

    Public Sub New(ByVal ip As IPEndPoint, Optional ByVal buffcount As Int64 = defaultBuffCount)
        MyBase.new(buffcount)
        _new(ip)
    End Sub

    Public Sub New(ByVal ip As IPEndPoint, ByVal holdtime As Int64, ByVal livetime As Int64, _
                   Optional ByVal buffcount As Int64 = defaultBuffCount)
        MyBase.New(buffcount)
        _new(ip, holdtime, livetime)
    End Sub

    Public Function hostport() As String
        Return displayHostPort(host, port)
    End Function
End Class